package com.hundsun.epay.autosimu.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import com.hundsun.epay.autosimu.config.Channel;
import com.hundsun.epay.autosimu.config.ControllerField;
import com.hundsun.epay.autosimu.config.ControllerResp;
import com.hundsun.epay.autosimu.config.PayCorporation;
import com.hundsun.epay.autosimu.config.Trade;
import com.hundsun.epay.autosimu.enums.CommunicationMethod;
import com.hundsun.epay.autosimu.enums.ControllerFieldType;
import com.hundsun.epay.autosimu.enums.TradeType;
import com.hundsun.epay.autosimu.internal.ControlRespPackaging;
import com.hundsun.epay.autosimu.internal.ObjectFactory;
import com.hundsun.epay.autosimu.utils.StringUtility;

public abstract class IntrospectedTrade {
	protected enum InternalAttributes{
		ATTR_TRADE_METHOD_NAME,   //方法名
		ATTR_NOTIFICATION_METHOD_NAME,   //异步通知方法名，主要可能用于网关
		ATTR_NOTIFY_SYNC,
		ATTR_COMMUNICATION_METHOD,
		ATTR_TRADE_TYPE,
		ATTR_CONTROL_ORDER_NO,
		ATTR_CONTROL_DETAIL_NO,
		ATTR_CONTROL_CONTROL_FIELD,
		ATTR_CONTROL_BACKURL_FIELD,
		ATTR_CONTROL_FRONTURL_FIELD,
		ATTR_CONTROL_EXPORT_FIELD,
		ATTR_CONTROL_DEFAULT_RESP,
		ATTR_CONTROL_CONDITIONAL_RESP,
		ATTR_CONTROL_REF_FIELD,
		ATTR_CONTROL_CURRENT_REF_FIELD,
	}
	
	protected Map<InternalAttributes, Object> internalAttributes;
	
	protected Channel channel;
	protected Trade trade;
	protected PayCorporation payCorp;
	protected IntrospectedChannel introspectedChannel;
	public IntrospectedTrade() {
		internalAttributes = new HashMap<IntrospectedTrade.InternalAttributes, Object>();
	}
	public Channel getChannel() {
		return channel;
	}
	public void setChannel(Channel channel) {
		this.channel = channel;
	}
	public Trade getTrade() {
		return trade;
	}
	public void setTrade(Trade trade) {
		this.trade = trade;
	}
	public PayCorporation getPayCorp() {
		return payCorp;
	}
	public void setPayCorp(PayCorporation payCorp) {
		this.payCorp = payCorp;
	}
	public IntrospectedChannel getIntrospectedChannel() {
		return introspectedChannel;
	}
	public void setIntrospectedChannel(IntrospectedChannel introspectedChannel) {
		this.introspectedChannel = introspectedChannel;
	}
	
	public void initialize(){
		calculateTradeAttribute();
		calculateControlAttribute();
	}
	
	protected void calculateControlAttribute(){
		calculateFieldControlAttribute();
		calculateAndInitRespControlAttribute();
	}
	
	protected void calculateFieldControlAttribute(){
		for (Entry<String, ControllerField> entry : trade.getCfields().entrySet()) {
			calculateFieldControl(entry.getValue());
		}
	}
	
	protected void calculateAndInitRespControlAttribute(){
		setDefaultControlResp(calculateAndInitControlResp(trade.getDefaultCResp()));
		
		for (ControllerResp cresp : trade.getCresps()) {
			addConditionalControlResp(calculateAndInitControlResp(cresp));
		}
	}
	
	protected ControlRespPackaging calculateAndInitControlResp(ControllerResp cresp){
		ControlRespPackaging packing = ObjectFactory.createControlRespPacking(cresp);
		packing.initialize();
		return packing;
	}
	
	protected void calculateFieldControl(ControllerField field){
		String wholdId = calculateControlFieldWholeId(field);
		ControllerFieldType type = field.getType();
		if(ControllerFieldType.ORDER_NO.equals(type)){
			setOrderNoControlField(wholdId);
		}else if(ControllerFieldType.DETAIL_NO.equals(type)){
			setDetailNoControlField(wholdId);
		}else if(ControllerFieldType.CONTROLLER.equals(type)){
			if(field.isControl()){
				addCurrentControlField(wholdId);
			}else{
				addExportControlField(wholdId);
			}
		}else if(ControllerFieldType.BACKURL.equals(type)){
			setBackUrlControlField(wholdId);
		}else if(ControllerFieldType.FRONTURL.equals(type)){
			setFrontUrlControlField(wholdId);
		}else if(ControllerFieldType.REF.equals(type)){
			if(StringUtility.stringHasValue(field.getRefField())){
				if(field.isControl()){
					addCurrentRefControlField(wholdId);
				}
				addRefControlField(wholdId);
			}
		}else{
			throw new RuntimeException("Unsupport control field type.");
		}
	}
	
	protected String calculateControlFieldWholeId(ControllerField field){
		return trade.getId() + "." + field.getId();
	}
	
	protected void calculateTradeAttribute(){
		setTradeCommunicationMethod(calculateCommunicationMethod());
		setTradeMethodNameAttribute(calculateTradeMethodName());
		setTradeNotificationMethodName(calculateTradeNotificationName());
		setTradeType(trade.getType());
		setNotifySync(trade.getSync());
	}
	
	protected CommunicationMethod calculateCommunicationMethod(){
		if(trade.getCommMethod() != null){
			return trade.getCommMethod();
		}else if(channel.getCommMethod() != null){
			return channel.getCommMethod();
		}else if(payCorp.getCommMethod() != null){
			return payCorp.getCommMethod();
		}else{
			throw new RuntimeException("Need a appointed communication method");
		}
	}
	
	protected String calculateTradeMethodName(){
		if(StringUtility.isBlank(trade.getMethodName())){
			return generateTradeMethodName(trade.getId());
		}else{
			return trade.getMethodName();
		}
	}
	
	/**
	 * 根据交易id生成交易实现方法名称
	 * @param tradeId
	 * @return
	 */
	protected String generateTradeMethodName(String tradeId){
		return StringUtility.convertFristLetterCase(StringUtility.trimUntilFirstLetter(StringUtility.lineToCamel(tradeId)), false);
	}
	
	/**
	 * 计算通知交易名称，根据属性notify  notifyName判断
	 * @return
	 */
	protected String calculateTradeNotificationName(){
		if(StringUtility.isNotBlank(trade.getNotifyName())){
			return trade.getNotifyName();
		}else if(StringUtility.isNotBlank(trade.getNotify())){
			return generateTradeMethodName(trade.getNotify());
		}else{
			return null;    //返回空，不支持异步通知
		}
	}
	
	public void setTradeType(TradeType type){
		internalAttributes.put(InternalAttributes.ATTR_TRADE_TYPE, type);
	}
	
	public void setTradeMethodNameAttribute(String type){
		internalAttributes.put(InternalAttributes.ATTR_TRADE_METHOD_NAME, type);
	}
	
	public void setTradeNotificationMethodName(String type){
		internalAttributes.put(InternalAttributes.ATTR_NOTIFICATION_METHOD_NAME, type);
	}
	
	public void setTradeCommunicationMethod(CommunicationMethod method){
		internalAttributes.put(InternalAttributes.ATTR_COMMUNICATION_METHOD, method);
	}
	
	public String getTradeMethodNameAttribute(){
		return (String) internalAttributes.get(InternalAttributes.ATTR_TRADE_METHOD_NAME);
	}
	
	public String getTradeNotificationMethodName(){
		return (String) internalAttributes.get(InternalAttributes.ATTR_NOTIFICATION_METHOD_NAME);
	}
	
	public CommunicationMethod getTradeCommunicationMethod(){
		return (CommunicationMethod) internalAttributes.get(InternalAttributes.ATTR_COMMUNICATION_METHOD);
	}
	
	public TradeType getTradeType(){
		return (TradeType) internalAttributes.get(InternalAttributes.ATTR_TRADE_TYPE);
	}
	
	public void setOrderNoControlField(String cfieldId){
		internalAttributes.put(InternalAttributes.ATTR_CONTROL_ORDER_NO, cfieldId);
	}
	
	public String getOrderNoControlField(){
		return (String) internalAttributes.get(InternalAttributes.ATTR_CONTROL_ORDER_NO);
	}
	
	public void setDetailNoControlField(String cfieldId){
		internalAttributes.put(InternalAttributes.ATTR_CONTROL_DETAIL_NO, cfieldId);
	}
	
	public String getDetailNoControlField(){
		return (String) internalAttributes.get(InternalAttributes.ATTR_CONTROL_DETAIL_NO);
	}
	
	public void addCurrentControlField(String cfieldId){
		Set<String> cfieldIds = getCurrentControlField();
		if(cfieldIds == null){
			cfieldIds = new TreeSet<String>();
			internalAttributes.put(InternalAttributes.ATTR_CONTROL_CONTROL_FIELD, cfieldIds);
		}
		cfieldIds.add(cfieldId);
	}
	
	@SuppressWarnings("unchecked")
	public Set<String> getCurrentControlField(){
		return (Set<String>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_CONTROL_FIELD);
	}
	
	@SuppressWarnings("unchecked")
	public Set<String> getRefControlField(){
		return (Set<String>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_REF_FIELD);
	}
	
	public void addRefControlField(String cfieldId){
		Set<String> cfieldIds = getRefControlField();
		if(cfieldIds == null){
			cfieldIds = new TreeSet<String>();
			internalAttributes.put(InternalAttributes.ATTR_CONTROL_REF_FIELD, cfieldIds);
		}
		cfieldIds.add(cfieldId);
	}
	
	@SuppressWarnings("unchecked")
	public Set<String> getCurrentRefControlField(){
		return (Set<String>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_CURRENT_REF_FIELD);
	}
	
	public void addCurrentRefControlField(String cfieldId){
		Set<String> cfieldIds = getCurrentRefControlField();
		if(cfieldIds == null){
			cfieldIds = new TreeSet<String>();
			internalAttributes.put(InternalAttributes.ATTR_CONTROL_CURRENT_REF_FIELD, cfieldIds);
		}
		cfieldIds.add(cfieldId);
	}
	
	public void addExportControlField(String cfieldId){
		Set<String> cfieldIds = getExportControlField();
		if(cfieldIds == null){
			cfieldIds = new TreeSet<String>();
			internalAttributes.put(InternalAttributes.ATTR_CONTROL_EXPORT_FIELD, cfieldIds);
		}
		cfieldIds.add(cfieldId);
	}
	
	@SuppressWarnings("unchecked")
	public Set<String> getExportControlField(){
		return (Set<String>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_EXPORT_FIELD);
	}
	
	public void setBackUrlControlField(String cfieldId){
		internalAttributes.put(InternalAttributes.ATTR_CONTROL_BACKURL_FIELD, cfieldId);
	}
	
	public String getBackUrlControlField(){
		return (String) internalAttributes.get(InternalAttributes.ATTR_CONTROL_BACKURL_FIELD);
	}
	
	public void setFrontUrlControlField(String cfieldId){
		internalAttributes.put(InternalAttributes.ATTR_CONTROL_FRONTURL_FIELD, cfieldId);
	}
	
	public String getFrontUrlControlField(){
		return (String) internalAttributes.get(InternalAttributes.ATTR_CONTROL_FRONTURL_FIELD);
	}
	
	public void setDefaultControlResp(ControlRespPackaging packing){
		internalAttributes.put(InternalAttributes.ATTR_CONTROL_DEFAULT_RESP, packing);
	}
	
	public ControlRespPackaging getDefaultControlResp(){
		return (ControlRespPackaging) internalAttributes.get(InternalAttributes.ATTR_CONTROL_DEFAULT_RESP);
	}
	
	public void addConditionalControlResp(ControlRespPackaging packing){
		List<ControlRespPackaging> packings = getConditionalControlResp();
		if(packings == null){
			packings = new ArrayList<ControlRespPackaging>();
			internalAttributes.put(InternalAttributes.ATTR_CONTROL_CONDITIONAL_RESP, packings);
		}
		packings.add(packing);
	}
	
	@SuppressWarnings("unchecked")
	public List<ControlRespPackaging> getConditionalControlResp(){
		return (List<ControlRespPackaging>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_CONDITIONAL_RESP);
	}
	
	public void setNotifySync(Boolean sync){
		internalAttributes.put(InternalAttributes.ATTR_NOTIFY_SYNC, sync);
	}
	
	public Boolean getNotifySync(){
		return (Boolean) internalAttributes.get(InternalAttributes.ATTR_NOTIFY_SYNC);
	}
}
